package hos.map.distance;

import java.util.List;

import hos.map.coordinate.LatLng;
import hos.map.tool.CalculatorUtils;

/**
 * <p>Title: DistanceCalculator </p>
 * <p>Description: 距离计算接口 </p>
 * <p>Company: www.mapuni.com </p>
 *
 * @author : 蔡俊峰
 * @version : 1.0
 * @date : 2023-12-10 22:57
 */
public interface DistanceCalculator {
    /**
     * 计算2点位之间的距离
     *
     * @param p1 第一个点位
     * @param p2 第二个点位
     * @return 距离
     */
    BearingDistanceCache distance(final LatLng p1, final LatLng p2);

    /**
     * 对目标点位进行偏移
     *
     * @param from            目标点位
     * @param distanceInMeter 偏移的距离
     * @param bearing         偏移的角度
     * @return 偏移后的点位
     */
    LatLng offset(
            final LatLng from, final double distanceInMeter, final double bearing);

    /**
     * 判断点位是否在集合形成的面内部
     *
     * @param point 目标点位
     * @return true 在多边形的内部
     */
    boolean isPolygonContains(List<LatLng> polygon, LatLng point);

    /**
     * 计算给定路径在地球上的长度，以米为单位。
     */
    default double distance(List<LatLng> path) {
        if (path == null || path.size() < 2) {
            return 0;
        }
        double length = 0;
        int size = path.size();
        for (int i = 0; i < size - 1; i++) {
            LatLng current = path.get(i);
            LatLng next = path.get(i + 1);
            BearingDistanceCache distance = distance(current, next);
            if (distance != null) {
                length += distance.getDistance();
            }
        }
        return length;
    }

    /**
     * 计算给定路径在地球上的长度
     */
    default double distance(LengthUnit unit, List<LatLng> path) {
        if (path == null || path.size() < 2) {
            return 0;
        }
        final double dist = distance(path);
        // If the distance is NaN or infinite, return 0.0
        if (Double.isNaN(dist) || Double.isInfinite(dist)) {
            return 0;
        }
        return LengthUnit.Meter().to(unit, dist);
    }

    /**
     * 计算2点位之间的距离
     *
     * @param p1 第一个点位
     * @param p2 第二个点位
     * @return 距离
     */
    default BearingDistanceCache distance(LengthUnit unit, LatLng p1, LatLng p2) {
        if (p1 == null || p2 == null) {
            return null;
        }
        BearingDistanceCache distance = distance(p1, p2);
        if (distance == null) {
            return null;
        }
        final double dist = distance.getDistance();
        // If the distance is NaN or infinite, return 0.0

        if (Double.isNaN(dist) || Double.isInfinite(dist)) {
            return null;
        }
        distance.setDistance(LengthUnit.Meter().to(unit, dist));
        return distance;
    }

    /**
     * 计算2个点位之间的角度
     */
    default double bearing(LatLng p1, LatLng p2) {
        return CalculatorUtils.bearing(p1, p2);
    }

    /**
     * 计算点是否在圆内
     *
     * @param center 圆心
     * @param radius 半径
     * @param point  点位
     */
    default boolean isPointInside(final LatLng center, int radius, final LatLng point) {
        if (center == null || point == null) {
            return false;
        }
        BearingDistanceCache distance = distance(center, point);
        if (distance == null) {
            return false;
        }
        final double dist = distance.getDistance();
        return dist <= radius;
    }
}
